a tool for shared writing and social publishing
1import { z } from "zod"; 2import { 3 PullRequest, 4 PullResponseV1, 5 VersionNotSupportedResponse, 6} from "replicache"; 7import type { Fact } from "src/replicache"; 8import { FactWithIndexes } from "src/replicache/utils"; 9import type { Attribute } from "src/replicache/attributes"; 10import { makeRoute } from "../lib"; 11import type { Env } from "./route"; 12 13// First define the sub-types for V0 and V1 requests 14const pullRequestV0 = z.object({ 15 pullVersion: z.literal(0), 16 schemaVersion: z.string(), 17 profileID: z.string(), 18 cookie: z.any(), // ReadonlyJSONValue 19 clientID: z.string(), 20 lastMutationID: z.number(), 21}); 22 23// For the Cookie type used in V1 24const cookieType = z.union([ 25 z.null(), 26 z.string(), 27 z.number(), 28 z 29 .object({ 30 order: z.union([z.string(), z.number()]), 31 }) 32 .and(z.record(z.string(), z.any())), // ReadonlyJSONValue with order property 33]); 34 35const pullRequestV1 = z.object({ 36 pullVersion: z.literal(1), 37 schemaVersion: z.string(), 38 profileID: z.string(), 39 cookie: cookieType, 40 clientGroupID: z.string(), 41}); 42 43// Combined PullRequest type 44const PullRequestSchema = z.union([pullRequestV0, pullRequestV1]); 45 46export const pull = makeRoute({ 47 route: "pull", 48 input: z.object({ pullRequest: PullRequestSchema, token_id: z.string() }), 49 handler: async ({ pullRequest, token_id }, { supabase }: Env) => { 50 let body = pullRequest; 51 if (body.pullVersion === 0) return versionNotSupported; 52 let { data, error } = await supabase.rpc("pull_data", { 53 token_id, 54 client_group_id: body.clientGroupID, 55 }); 56 if (!data) { 57 console.log(error); 58 59 return { 60 error: "ClientStateNotFound", 61 } as const; 62 } 63 64 let facts = data.facts as { 65 attribute: string; 66 created_at: string; 67 data: any; 68 entity: string; 69 id: string; 70 updated_at: string | null; 71 version: number; 72 }[]; 73 let publication_data = data.publications as { 74 description: string; 75 title: string; 76 }[]; 77 let pub_patch = publication_data?.[0] 78 ? [ 79 { 80 op: "put", 81 key: "publication_description", 82 value: publication_data[0].description, 83 }, 84 { 85 op: "put", 86 key: "publication_title", 87 value: publication_data[0].title, 88 }, 89 ] 90 : []; 91 92 let clientGroup = ( 93 (data.client_groups as { 94 client_id: string; 95 client_group: string; 96 last_mutation: number; 97 }[]) || [] 98 ).reduce( 99 (acc, clientRecord) => { 100 acc[clientRecord.client_id] = clientRecord.last_mutation; 101 return acc; 102 }, 103 {} as { [clientID: string]: number }, 104 ); 105 106 return { 107 cookie: Date.now(), 108 lastMutationIDChanges: clientGroup, 109 patch: [ 110 { op: "clear" }, 111 { op: "put", key: "initialized", value: true }, 112 ...(facts || []).map((f) => { 113 return { 114 op: "put", 115 key: f.id, 116 value: FactWithIndexes(f as unknown as Fact<Attribute>), 117 } as const; 118 }), 119 ...pub_patch, 120 ], 121 } as PullResponseV1; 122 }, 123}); 124 125const versionNotSupported: VersionNotSupportedResponse = { 126 error: "VersionNotSupported", 127 versionType: "pull", 128};